package com.nineclock.attendance.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nineclock.attendance.dto.AttendGroupDTO;
import com.nineclock.attendance.dto.AttendGroupPCExtraConfigDTO;
import com.nineclock.attendance.dto.AttendGroupPartDTO;
import com.nineclock.attendance.enums.AttendEnums;
import com.nineclock.attendance.mapper.AttendGroupMapper;
import com.nineclock.attendance.mapper.AttendGroupPartMapper;
import com.nineclock.attendance.pojo.AttendGroup;
import com.nineclock.attendance.pojo.AttendGroupPart;
import com.nineclock.attendance.service.AttendGroupService;
import com.nineclock.common.entity.PageResult;
import com.nineclock.common.enums.ResponseEnum;
import com.nineclock.common.exception.NcException;
import com.nineclock.common.filter.CurrentUserHolder;
import com.nineclock.common.utils.BeanHelper;
import com.nineclock.common.utils.DateTimeUtil;
import com.nineclock.system.feign.SysCompanyUserFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Transactional
public class AttendGroupServiceImpl implements AttendGroupService {

    @Autowired
    private AttendGroupMapper attendGroupMapper;
    
    @Autowired
    private SysCompanyUserFeign companyUserFeign;

    @Autowired
    private AttendGroupPartMapper attendGroupPartMapper;

    @Override
    public PageResult<AttendGroupDTO> queryAttendGroupPage(Integer page, Integer pageSize) {
        // 1). 查询当前企业是否初次使用 ------------> 先查，如果没有查询到就是初次使用
        LambdaQueryWrapper<AttendGroup> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AttendGroup::getCompanyId, CurrentUserHolder.get().getCompanyId());
        Integer count = attendGroupMapper.selectCount(wrapper);

        if(count == 0){
            // 2). 如果初次使用需要新增默认考勤组，设置企业所有部门都采用默认考勤组  (调用系统微服务获取企业所有部门)
            AttendGroup attendGroup = this.buildDefaultAttendGroup();
            attendGroupMapper.insert(attendGroup);

            List<Long> deptIdList = companyUserFeign.queryDepartmentIds().getData();
            
            // 往考勤组关联表插入数据，设置哪些部门要参加该考勤组
            if(!CollectionUtils.isEmpty(deptIdList)){
                for (Long deptId : deptIdList) {
                    AttendGroupPart attendGroupPart = new AttendGroupPart();
                    attendGroupPart.setAttendGroupId(attendGroup.getId());
                    attendGroupPart.setObjectType(AttendEnums.ATTEND_OBJECT_TYPE_DEP.value()); // 1-部门，2-员工
                    attendGroupPart.setObjectId(deptId); // 部门id
                    attendGroupPart.setAttendType(AttendEnums.ATTEND_TYPE_YES.value()); // 1-参加，2-不参加
                    attendGroupPartMapper.insert(attendGroupPart);
                }
            }
            
        }

        // 3). 分页查询企业考勤组列表数据
        IPage<AttendGroup> ipage = new Page<>(page, pageSize);
        ipage = attendGroupMapper.selectPage(ipage, wrapper);

        // 获取当前页数据
        List<AttendGroup> attendGroupList = ipage.getRecords();

        // 4). 遍历考勤组列表，获取企业考勤组中参与者数量 --- 区分是部门，还是员工，如果是部门要获取部门下的员工数量
        List<AttendGroupDTO> attendGroupDTOList = attendGroupList.stream().map(attendGroup -> {

            AttendGroupDTO attendGroupDTO = BeanHelper.copyProperties(attendGroup, AttendGroupDTO.class);

            // 根据考勤组id，查询考勤组关联表，获取有哪些部门/员工参与该考勤组
            LambdaQueryWrapper<AttendGroupPart> attendGroupPartWrapper = new LambdaQueryWrapper<>();
            attendGroupPartWrapper.eq(AttendGroupPart::getAttendGroupId, attendGroupDTO.getId());
            attendGroupPartWrapper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_YES.value());
            List<AttendGroupPart> attendGroupParts = attendGroupPartMapper.selectList(attendGroupPartWrapper);

            Integer sum = 0;
            List<Long> deptIdList = new ArrayList<>();
            for (AttendGroupPart attendGroupPart : attendGroupParts) {
                // 如果是员工，数量 + 1
                if(attendGroupPart.getObjectType() == AttendEnums.ATTEND_OBJECT_TYPE_USER.value()){
                    sum ++;
                }else{
                    // 如果是部门，统计部门id集合
                    deptIdList.add(attendGroupPart.getObjectId());
                }
            }

            // 根据部门id集合，调用系统微服务接口，获取这些部门下的员工数量
            Integer deptCounts = companyUserFeign.queryUserCountByDepartmentIds(deptIdList).getData();

            // 设置考勤组参与者数量
            attendGroupDTO.setMemberNum(sum + deptCounts);

            return attendGroupDTO;

        }).collect(Collectors.toList());

        // 5). 组装结果，并返回
        return new PageResult<>(ipage.getTotal(), ipage.getPages(), attendGroupDTOList);
    }


    // 构建默认的考勤组
    private AttendGroup buildDefaultAttendGroup() {
        AttendGroup attendGroup = new AttendGroup();
        attendGroup.setName("默认考勤组");
        attendGroup.setAddressRange(500); // 500米有效距离打卡
        attendGroup.setAllowLateMinutes(10); // 允许迟到分数
        attendGroup.setLateMinutes(30); // 允许旷工分数
        attendGroup.setCompanyId(CurrentUserHolder.get().getCompanyId());
        attendGroup.setLng(new BigDecimal("23.13514700000000000000"));// 广州黑马程序员
        attendGroup.setLat(new BigDecimal("113.43467700000000000000"));// 广州黑马程序员
        attendGroup.setAddressName("黑马程序培训中心(广州校区)");
        attendGroup.setAddress("广东省广州市天河区珠吉街道珠吉路58号黑马培训机构");
        attendGroup.setStartWorkTime("09:00:00"); // 上班时间
        attendGroup.setOffWorkTime("18:00:00"); // 下班时间
        attendGroup.setStartNoonRestTime("12:00:00"); // 中午休息开始时间
        attendGroup.setEndNoonRestTime("14:00:00"); // 中午休息结束时间
        attendGroup.setWorkdays("1,1,1,1,1,0,0");  // 工作日
        attendGroup.setCreateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setCreateTime(new Date());
        attendGroup.setUpdateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setUpdateTime(new Date());
        return attendGroup;
    }

    @Override
    public void addAttendGroup(AttendGroupDTO attendGroupDTO) {
        // 1.健壮性判断
        if(attendGroupDTO == null){
            throw new NcException(ResponseEnum.INVALID_PARAM_ERROR);
        }

        // 2.组装考勤组对象, 保存考勤组 ===> at_attend_group
        AttendGroup attendGroup = BeanHelper.copyProperties(attendGroupDTO, AttendGroup.class);
        // 2.1 设置考勤组关联的企业ID - 创建人ID - 更新人ID 等基本信息
        attendGroup.setCompanyId(CurrentUserHolder.get().getCompanyId());
        attendGroup.setCreateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setCreateTime(new Date());
        attendGroup.setUpdateUserId(CurrentUserHolder.get().getCompanyUserId());
        attendGroup.setUpdateTime(new Date());

        // 2.2 设置工作日，使用逗号 "," 分割
        if(!CollectionUtils.isEmpty(attendGroupDTO.getWorkdays())){
            String workDays = StrUtil.join(",", attendGroupDTO.getWorkdays().toArray());
            attendGroup.setWorkdays(workDays);
        }
        
        // 2.3 设置必须/不用打卡的日期，获取出来之后使用逗号","分割
        List<AttendGroupPCExtraConfigDTO> extraConfig = attendGroupDTO.getExtraConfig();
        if(!CollectionUtils.isEmpty(extraConfig)){

            List<String> nesessaryList = new ArrayList<>(); // 必须打卡的日期
            List<String> unnesessaryList = new ArrayList<>(); // 无需打卡的日期

            for (AttendGroupPCExtraConfigDTO configDTO : extraConfig) {
                // 将日期格式化
                String setDate = configDTO.getSetDate();
                String formatDateStr = DateTimeUtil.dateToStr(new Date(Long.valueOf(setDate)), DateTimeUtil.TIME_FORMAT_2);

                if(configDTO.getRequiredAttend() == 1){ // 必须打卡
                    nesessaryList.add(formatDateStr);
                } else{
                    unnesessaryList.add(formatDateStr);
                }
            }
            // 设置必须打卡、无需打卡的日期，以逗号分隔
            attendGroup.setNecessaryTimeList(StrUtil.join(",", nesessaryList.toArray()));
            attendGroup.setUnnecessaryTimeList(StrUtil.join(",", unnesessaryList.toArray()));
        }

        // 2.4 保存考勤组
        attendGroupMapper.insert(attendGroup);


        saveAttendGroupPart(attendGroupDTO, attendGroup.getId());

    }


    /**
     * 保存关联用户、部门到该考勤组 ===> at_attend_group_part
     */
    private void saveAttendGroupPart(AttendGroupDTO attendGroupDTO, Long attendGroupId) {

        // 3.1 需要参与考勤组的对象处理
        List<AttendGroupPartDTO> participates = attendGroupDTO.getParticipates();
        for (AttendGroupPartDTO participate : participates) {
            // 3.1.1 判断当前对象是否加入到某个考勤组（要求: 用户/部门只能加入一个考勤组，如果设置新考勤组，需要先删除旧的考勤组）
            LambdaQueryWrapper<AttendGroupPart> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AttendGroupPart::getObjectId, participate.getObjectId()); // 部门id或员工id
            wrapper.eq(AttendGroupPart::getObjectType, participate.getObjectType()); // 1-部门，2-员工
            wrapper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_YES.value()); // 参加

            Integer count = attendGroupPartMapper.selectCount(wrapper);

            if(count > 0){
                // 3.1.2 如果存在旧的考勤组, 需要删除旧考勤组
                attendGroupPartMapper.delete(wrapper);
            }
            // 3.1.3 给考勤对象关联新的考勤组
            AttendGroupPart attendGroupPart = new AttendGroupPart();
            attendGroupPart.setAttendGroupId(attendGroupId); // 新的考勤组id
            attendGroupPart.setObjectType(participate.getObjectType());
            attendGroupPart.setObjectId(participate.getObjectId());
            attendGroupPart.setAttendType(AttendEnums.ATTEND_TYPE_YES.value());
            attendGroupPartMapper.insert(attendGroupPart);
        }
        

        // 3.2 不需要参与考勤组的对象处理
        List<AttendGroupPartDTO> notParticipates = attendGroupDTO.getNotParticipates();

        for (AttendGroupPartDTO notParticipate : notParticipates) {

            // 3.2.1 判断用户/部门是否已经在无需参与考勤组中
            LambdaQueryWrapper<AttendGroupPart> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(AttendGroupPart::getObjectId, notParticipate.getObjectId()); // 部门id或员工id
            wrapper.eq(AttendGroupPart::getObjectType, notParticipate.getObjectType()); // 1-部门，2-员工
            wrapper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_NO.value()); // 不参加

            Integer count = attendGroupPartMapper.selectCount(wrapper);

            // 3.2.2 判定是否已经存在，如果不存在，则新增无需考勤组记录，如果存在，不需要处理
            if(count == 0){

                AttendGroupPart attendGroupPart = new AttendGroupPart();
                attendGroupPart.setAttendGroupId(attendGroupId); // 新的考勤组id
                attendGroupPart.setObjectType(notParticipate.getObjectType());
                attendGroupPart.setObjectId(notParticipate.getObjectId());
                attendGroupPart.setAttendType(AttendEnums.ATTEND_TYPE_NO.value());
                attendGroupPartMapper.insert(attendGroupPart);
            }
        }

    }


    @Override
    public AttendGroupDTO getAttendGroupByUserId() {
        // 获取当前登录用户的员工id
        Long companyUserId = CurrentUserHolder.get().getCompanyUserId();

        // 查询考勤组关联表，查询当前用户本身有没有加入某个考勤组
        LambdaQueryWrapper<AttendGroupPart> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AttendGroupPart::getObjectId, companyUserId);
        wrapper.eq(AttendGroupPart::getObjectType, AttendEnums.ATTEND_OBJECT_TYPE_USER.value());
        wrapper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_YES.value());
        AttendGroupPart attendGroupPart = attendGroupPartMapper.selectOne(wrapper);

        if(attendGroupPart == null){
            // 调用系统微服务，查询员工所属部门 --- 上级部门 --- 上上级....
            List<Long> deptIdList = companyUserFeign.queryDepartmentsByUserId(companyUserId).getData();

            for (Long deptId : deptIdList) {
                // 查询部门是否参加某个考勤组
                LambdaQueryWrapper<AttendGroupPart> deptWrapper = new LambdaQueryWrapper<>();
                deptWrapper.eq(AttendGroupPart::getObjectId, deptId);
                deptWrapper.eq(AttendGroupPart::getObjectType, AttendEnums.ATTEND_OBJECT_TYPE_DEP.value());
                deptWrapper.eq(AttendGroupPart::getAttendType, AttendEnums.ATTEND_TYPE_YES.value());
                attendGroupPart = attendGroupPartMapper.selectOne(deptWrapper);

                if(attendGroupPart!=null){
                    break;
                }
            }
        }

        // 未查询到考勤组，抛出异常
        if(attendGroupPart == null){
            throw new NcException(ResponseEnum.USER_NOT_MATCH_ATTENDGROUP);
        }

        // 根据考勤组id查询考勤组
        AttendGroup attendGroup = attendGroupMapper.selectById(attendGroupPart.getAttendGroupId());
        
        // 数据转换
        AttendGroupDTO attendGroupDTO = BeanHelper.copyProperties(attendGroup, AttendGroupDTO.class);

        // 额外设置工作日
        String[] workDaysArray = attendGroup.getWorkdays().split(",");
        attendGroupDTO.setWorkdays(Arrays.asList(workDaysArray));
        
        return attendGroupDTO;
    }

}
